﻿using System;
using System.Collections;
using UnityEngine;


namespace RStudio.Elevator
{
    /// <summary>
    /// 驱动器|动力源
    /// </summary>
    public class ElevatorDriven : IElevatorDriven
    {
        #region 接口|IElevatorDriven
        public ElevatorStateType StatusElevator { get; set; }
        public ElevatorRunningType StatusRunning { get; set; }

        public int CurrentFloorNo { get; set; }
        public bool IsDoorOpened { get; set; }
        #endregion

        public ElevatorComponent Component { get; private set; }

        public ElevatorDriven(ElevatorComponent elevatorComponent)
        {
            this.Component = elevatorComponent;

            this.StatusElevator = ElevatorStateType.IsWaiting;
            this.CurrentFloorNo = 0;
            this.IsDoorOpened = false;

            // 订阅 任务队列，实现驱动运行
            this.Component.Queue.OnQueueUpdated += OnDrivenEnabled;

            // 订阅 同步自己状态
            this.OnDrivenActive += OnActived;
            this.OnDrivenDeActive += OnDeActived;
        }


        /// <summary>
        /// 事件|驱动开始移动
        /// </summary>
        public event Action<QueueEvent> OnDrivenActive;

        /// <summary>
        /// 事件|驱动移动结束
        /// </summary>
        public event Action<QueueEvent> OnDrivenDeActive;

        /// <summary>
        /// 协程|驱动帧
        /// </summary>
        private Coroutine _currentCoroutine;

        /// <summary>
        /// 缓存|
        /// </summary>
        DataFloor _currentDartaFloor;


        /// <summary>
        /// 启动|电梯
        /// </summary>
        private void OnDrivenEnabled(QueueEvent @event)
        {
            if (_currentDartaFloor == null)
            {
                _currentDartaFloor = @event.Data;
                _currentCoroutine = Component.StartCoroutine(IMovementCoroutine(@event));

                return;
            }

            if (_currentDartaFloor == @event.Data)
            {
                return;
            }

            if (_currentCoroutine != null)
            {
                Component.StopCoroutine(_currentCoroutine);
            }
            _currentDartaFloor = @event.Data;
            _currentCoroutine = Component.StartCoroutine(IMovementCoroutine(@event));
        }



        #region 电梯移动逻辑

        /// <summary>
        /// 移动协程
        /// </summary>
        private IEnumerator IMovementCoroutine(QueueEvent @event)
        {
            Transform node = this.Component.gameObject.transform;
            Vector3 targetPos = new(node.position.x, @event.Data.FloorVertical, node.position.z);

            // 事件|电梯启动
            this.OnDrivenActive?.Invoke(@event);

            while (Vector3.Distance(node.position, targetPos) > 0.01f)
            {
                node.position = Vector3.MoveTowards(
                    node.position,
                    targetPos,
                    this.Component.Detail.moveSpeed * Time.deltaTime
                );
                yield return null;
            }

            // 1. 通知队列移除
            // 2. 同步当前楼层号
            // 3. 释放当前协程
            // 4. 强制同步位置
            this.Component.Queue.Remove(@event.QueueType);
            this.CurrentFloorNo = @event.Data.FloorNo;
            this._currentCoroutine = null;
            this._currentDartaFloor = null;
            node.position = targetPos;
 

            // 事件|电梯停止
            this.OnDrivenDeActive?.Invoke(@event);

            PerformDoorOperation(true);
        }

        /// <summary>
        /// 设置故障状态
        /// </summary>
        public void SetDamaged(bool isDamaged)
        {
            if (isDamaged)
            {
                if (_currentCoroutine != null)
                {
                    Component.StopCoroutine(_currentCoroutine);
                    _currentCoroutine = null;
                }
                StatusElevator = ElevatorStateType.IsDamaged;
            }
            else
            {
                StatusElevator = ElevatorStateType.IsWaiting;
            }
        }

        /// <summary>
        /// 更新|电梯到达状态
        /// </summary>
        /// <param name="arg"></param>
        private void OnDeActived(QueueEvent arg)
        {
            this.SetStatus(ElevatorStateType.IsWaiting);
            this.SetStatusRunning(ElevatorRunningType.UnKnown);
        }

        /// <summary>
        /// 更新|电梯状态
        /// </summary>
        private void OnActived(QueueEvent @event)
        {
            this.SetStatusRunning(@event.QueueType switch
            {
                ElevatorQueueType.isQueueUp => ElevatorRunningType.IsUpper,
                ElevatorQueueType.isQueueDown => ElevatorRunningType.IsDown,
                ElevatorQueueType.UnKnown => ElevatorRunningType.UnKnown,
                _ => throw new NotImplementedException()
            });

            this.SetStatus(@event.QueueType switch
            {
                ElevatorQueueType.isQueueUp => ElevatorStateType.IsRunning,
                ElevatorQueueType.isQueueDown => ElevatorStateType.IsRunning,
                ElevatorQueueType.UnKnown => ElevatorStateType.UnKnown,
                _ => throw new NotImplementedException()
            });
        }
        #endregion

        #region 门开关逻辑

        /// <summary>
        /// 开关门协程
        /// </summary>
        private IEnumerator DoorOperationCoroutine(bool open)
        {
            float timer = 0;
            while (timer < this.Component.Detail.doorOpenDuration)
            {
                timer += Time.deltaTime;

                yield return null;
            }

            IsDoorOpened = open;
            _currentCoroutine = null;

            this.Component.Queue.Notice();
        }

        /// <summary>
        /// 执行开关门流程
        /// </summary>
        public void PerformDoorOperation(bool open)
        {
            if (_currentCoroutine != null)
                Component.StopCoroutine(_currentCoroutine);

            _currentCoroutine = Component.StartCoroutine(DoorOperationCoroutine(open));
        }

        #endregion






        private ElevatorDriven SetStatus(ElevatorStateType stateType)
        {
            this.StatusElevator = stateType;
            return this;
        }
        private ElevatorDriven SetStatusRunning(ElevatorRunningType runningType)
        {
            this.StatusRunning = runningType;
            return this;
        }
    }
}
